package org.kernely.security.ui.user.mgmt;

import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;

import org.apache.shiro.crypto.hash.Sha256Hash;
import org.hibernate.exception.ConstraintViolationException;
import org.kernely.security.dao.UserDAO;
import org.kernely.security.dao.UserDetailsDAO;
import org.kernely.security.persistence.Group;
import org.kernely.security.persistence.Role;
import org.kernely.security.persistence.User;
import org.kernely.security.persistence.UserDetails;
import org.kernely.security.ui.SecurityUIBundle;
import org.kernely.ui.mvp.KernelyPresenter;
import org.kernely.ui.utils.KernelyNotification;
import org.osgi.service.event.Event;

import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.terminal.UserError;
import com.vaadin.ui.Button;
import com.vaadin.ui.Layout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Window.Notification;

public class UserManagementWindowPresenter extends KernelyPresenter<UserManagementWindowView>{

	/**
	 * The UserDetails to edit in edition mode
	 */
	private UserDetails ud;
	private boolean editMode = false;

	/**
	 * The add/edit form associated to this window
	 */
	private AddEditUserManagementView umv;
	private AddEditUserManagementPresenter ump;

	/**
	 * The add/edit role form associated to this window
	 */
	private EditUserRoleView eurv;
	private EditUserRolePresenter eurp;

	/**
	 * Default constructor in creation mode
	 */
	public UserManagementWindowPresenter(){}

	/**
	 * Constructor for the edition mode
	 * @param ud : the UserDetails to edit
	 */
	public UserManagementWindowPresenter(UserDetails ud){
		this.ud = ud;
		this.editMode = true;
	}

	/**
	 * @see org.kernely.ui.mvp.IKernelyPresenter#bind()
	 */
	@Override
	public final void bind() {

		// Add Mode
		if(ud == null){
			ump = new AddEditUserManagementPresenter();
			eurp = new EditUserRolePresenter();
		}
		// edit Mode
		else{
			ump = new AddEditUserManagementPresenter(ud);
			eurp = new EditUserRolePresenter(ud);
		}
		umv = new AddEditUserManagementView();
		eurv = new EditUserRoleView();
		ump.setView(umv);
		eurp.setView(eurv);

		view.getTabsheet().addTab((Layout)ump.getView(), "Informations", null);
		view.getTabsheet().addTab((Layout)eurp.getView(), "Roles", null);

		// Add a listener to the save button
		this.view.getSaveButton().addListener(new Button.ClickListener() {
			/**
			 * The Serial Version ID
			 */
			private static final long serialVersionUID = -515612812186312L;

			/**
			 * Action when the button is clicked
			 */
			public void buttonClick(ClickEvent event) {
				if(!SecurityUIBundle.getSecurityService().getCurrentUser().hasOneOf(Role.ROLE_ADMINISTRATOR)){
					view.getWindow().getParent().showNotification("Error", "You are not an administrator !", Notification.TYPE_ERROR_MESSAGE);
				}
				else{
					save();
				}
			}
		});

		// Add a listener to the cancel button
		this.view.getCancelButton().addListener(new Button.ClickListener() {
			/**
			 * The Serial Version ID
			 */
			private static final long serialVersionUID = -515612812186312L;

			/**
			 * Action when the button is clicked
			 */
			public void buttonClick(ClickEvent event) {
				// Close the window
				Window win = (Window)view.getParent();
				win.getParent().removeWindow(win);
			}
		});
	}

	/**
	 * Save an user and all informations/roles into database
	 */
	private void save(){
		try{
			// Check all fields
			umv.getLogin().validate();
			umv.getName().validate();
			umv.getFirstname().validate();
			umv.getMail().validate();
			if(!editMode){
				umv.getPassword().validate();
			}

			User user = new User();
			UserDAO udao = UserDAO.getInstance();
			UserDetails userDetails = new UserDetails();
			UserDetailsDAO uddao = UserDetailsDAO.getInstance();

			user.setLogin((String)umv.getLogin().getValue());

			// Password encryption
			String encodedPwd;
			if(!editMode){
				encodedPwd = new Sha256Hash((String)umv.getPassword().getValue()).toString();
			}
			else{
				if(umv.getPassword().getValue() != null && !umv.getPassword().getValue().toString().equals("null") && !umv.getPassword().getValue().toString().equals("")){
					encodedPwd = new Sha256Hash((String)umv.getPassword().getValue()).toString();
				}
				else{
					encodedPwd = ud.getUser().getPassword();
				}
			}
			user.setPassword(encodedPwd);

			// Retrieving roles
			user.setRoles(eurp.getUserRoles());
			

			// Save the user, or update in edit mode
			if(editMode){
				user.setId(ud.getUser().getId());
				user.setEnabled(ud.getUser().getEnabled());
				udao.update(user);
			}
			else{
				// By default, the user is enabled and he is not in a group.
				user.setGroups(new HashSet<Group>());
				user.setEnabled(1);
				udao.save(user);	
			}
			// Save or update in edit mode user details
			if(editMode){
				ud.setName((String)umv.getName().getValue());
				ud.setFirstname((String)umv.getFirstname().getValue());
				ud.setMail((String)umv.getMail().getValue());
				ud.setCivility((Integer)umv.getCivility().getValue());
				ud.setUser(udao.findUser((String)umv.getLogin().getValue()));
				uddao.updateUserDetails(ud);
			}
			else{
				userDetails.setName((String)umv.getName().getValue());
				userDetails.setFirstname((String)umv.getFirstname().getValue());
				userDetails.setMail((String)umv.getMail().getValue());
				userDetails.setCivility((Integer)umv.getCivility().getValue());
				userDetails.setUser(udao.findUser((String)umv.getLogin().getValue()));
				uddao.saveUserDetails(userDetails);
			}

			// We send an event to other bundles to notify them of the add/edit of an user.
			// Set the properties of the event
			Dictionary<String, Object> properties = new Hashtable<String,Object>();
			properties.put("path" , this.getClass().getName()+".save");
			properties.put("time", System.currentTimeMillis());
			properties.put("user", user);
			if (editMode){
				properties.put("title", SecurityUIBundle.EDIT_USER);
			} else {
				properties.put("title", SecurityUIBundle.NEW_USER);
			}
			//Send event
			Event userEvent = new Event("org/kernely/security/ui", properties);
			SecurityUIBundle.getEventAdminService().postEvent(userEvent);

			// Send an UI event which contains the user added.
			ManagementUserEvent event = new ManagementUserEvent(this,user);
			SecurityUIBundle.getEventBus().broadcastEvent(event);

			//Inform the user
			KernelyNotification notification;
			if (editMode){
				notification = new KernelyNotification("User " + umv.getFirstname().getValue() + " " + umv.getName().getValue() + " successfully updated.",KernelyNotification.INFO_ICON);
			} else {
				notification = new KernelyNotification("User " + umv.getFirstname().getValue() + " " + umv.getName().getValue() + " successfully created.",KernelyNotification.INFO_ICON);
			}
			view.getWindow().getParent().showNotification(notification);

			// Close the window when operation succeded
			Window win = (Window)view.getParent();
			win.getParent().removeWindow(win);
		}
		catch (InvalidValueException e){
			//Log the failure on screen, to give feedback to the user
			view.getWindow().showNotification(new KernelyNotification(e.getMessage(), KernelyNotification.ERROR_ICON));
		}
		catch (ConstraintViolationException cve){
			//Display the failure on screen an on the component
			view.getWindow().showNotification(new KernelyNotification("An user with the same name and/or login already exists.", KernelyNotification.ERROR_ICON));
			umv.getLogin().setComponentError(new UserError("An user with the same login and/or mail already exists."));
			umv.getMail().setComponentError(new UserError("An user with the same login and/or mail already exists."));
		}
	}
}
